
#include "nane/SoundSystem/OAL/OpenALAPI.h"
#include "nane/core/types.h"
#include "nane/core/log.h"

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool OpenALAPI::valid = false;
    ModuleHandle* OpenALAPI::hModule = NULL;
    LPALENABLE OpenALAPI::alEnable = NULL;
    LPALDISABLE OpenALAPI::alDisable = NULL;
    LPALISENABLED OpenALAPI::alIsEnabled = NULL;
    LPALGETBOOLEAN OpenALAPI::alGetBoolean = NULL;
    LPALGETINTEGER OpenALAPI::alGetInteger = NULL;
    LPALGETFLOAT OpenALAPI::alGetFloat = NULL;
    LPALGETDOUBLE OpenALAPI::alGetDouble = NULL;
    LPALGETBOOLEANV OpenALAPI::alGetBooleanv = NULL;
    LPALGETINTEGERV OpenALAPI::alGetIntegerv = NULL;
    LPALGETFLOATV OpenALAPI::alGetFloatv = NULL;
    LPALGETDOUBLEV OpenALAPI::alGetDoublev = NULL;
    LPALGETSTRING OpenALAPI::alGetString = NULL;
    LPALGETERROR OpenALAPI::alGetError = NULL;
    LPALISEXTENSIONPRESENT OpenALAPI::alIsExtensionPresent = NULL;
    LPALGETPROCADDRESS OpenALAPI::alGetProcAddress = NULL;
    LPALGETENUMVALUE OpenALAPI::alGetEnumValue = NULL;
    LPALLISTENERI OpenALAPI::alListeneri = NULL;
    LPALLISTENERF OpenALAPI::alListenerf = NULL;
    LPALLISTENER3F OpenALAPI::alListener3f = NULL;
    LPALLISTENERFV OpenALAPI::alListenerfv = NULL;
    LPALGETLISTENERI OpenALAPI::alGetListeneri = NULL;
    LPALGETLISTENERF OpenALAPI::alGetListenerf = NULL;
    LPALGETLISTENER3F OpenALAPI::alGetListener3f = NULL;
    LPALGETLISTENERFV OpenALAPI::alGetListenerfv = NULL;
    LPALGENSOURCES OpenALAPI::alGenSources = NULL;
    LPALDELETESOURCES OpenALAPI::alDeleteSources = NULL;
    LPALISSOURCE OpenALAPI::alIsSource = NULL;
    LPALSOURCEI OpenALAPI::alSourcei = NULL;
    LPALSOURCEF OpenALAPI::alSourcef = NULL;
    LPALSOURCE3F OpenALAPI::alSource3f = NULL;
    LPALSOURCEFV OpenALAPI::alSourcefv = NULL;
    LPALGETSOURCEI OpenALAPI::alGetSourcei = NULL;
    LPALGETSOURCEF OpenALAPI::alGetSourcef = NULL;
    LPALGETSOURCEFV OpenALAPI::alGetSourcefv = NULL;
    LPALSOURCEPLAYV OpenALAPI::alSourcePlayv = NULL;
    LPALSOURCESTOPV OpenALAPI::alSourceStopv = NULL;
    LPALSOURCESTOPV OpenALAPI::alSourceRewindv = NULL;
    LPALSOURCEPLAY OpenALAPI::alSourcePlay = NULL;
    LPALSOURCEPAUSE OpenALAPI::alSourcePause = NULL;
    LPALSOURCESTOP OpenALAPI::alSourceStop = NULL;
    LPALSOURCESTOP OpenALAPI::alSourceRewind = NULL;
    LPALGENBUFFERS OpenALAPI::alGenBuffers = NULL;
    LPALDELETEBUFFERS OpenALAPI::alDeleteBuffers = NULL;
    LPALISBUFFER OpenALAPI::alIsBuffer = NULL;
    LPALBUFFERDATA OpenALAPI::alBufferData = NULL;
    LPALGETBUFFERI OpenALAPI::alGetBufferi = NULL;
    LPALGETBUFFERF OpenALAPI::alGetBufferf = NULL;
    LPALSOURCEQUEUEBUFFERS OpenALAPI::alSourceQueueBuffers = NULL;
    LPALSOURCEUNQUEUEBUFFERS OpenALAPI::alSourceUnqueueBuffers = NULL;
    LPALDISTANCEMODEL OpenALAPI::alDistanceModel = NULL;
    LPALDOPPLERFACTOR OpenALAPI::alDopplerFactor = NULL;
    LPALDOPPLERVELOCITY OpenALAPI::alDopplerVelocity = NULL;
    LPALCGETSTRING OpenALAPI::alcGetString = NULL;
    LPALCGETINTEGERV OpenALAPI::alcGetIntegerv = NULL;
    LPALCOPENDEVICE OpenALAPI::alcOpenDevice = NULL;
    LPALCCLOSEDEVICE OpenALAPI::alcCloseDevice = NULL;
    LPALCCREATECONTEXT OpenALAPI::alcCreateContext = NULL;
    LPALCMAKECONTEXTCURRENT OpenALAPI::alcMakeContextCurrent = NULL;
    LPALCPROCESSCONTEXT OpenALAPI::alcProcessContext = NULL;
    LPALCGETCURRENTCONTEXT OpenALAPI::alcGetCurrentContext = NULL;
    LPALCGETCONTEXTSDEVICE OpenALAPI::alcGetContextsDevice = NULL;
    LPALCSUSPENDCONTEXT OpenALAPI::alcSuspendContext = NULL;
    LPALCDESTROYCONTEXT OpenALAPI::alcDestroyContext = NULL;
    LPALCGETERROR OpenALAPI::alcGetError = NULL;
    LPALCISEXTENSIONPRESENT OpenALAPI::alcIsExtensionPresent = NULL;
    LPALCGETPROCADDRESS OpenALAPI::alcGetProcAddress = NULL;
    LPALCGETENUMVALUE OpenALAPI::alcGetEnumValue = NULL;
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void CheckForOpenALError()
    {
        if( OpenALAPI::valid == true )
        {
            ALenum oalError = OpenALAPI::alGetError();
            if( oalError != AL_NO_ERROR )
            {
                LOG_ERROR(OpenALAPI::alGetString(oalError));
            }
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool InitializeOpenALAPI( const char* _moduleName )
    {
        OpenALAPI::hModule = DynamicModule::LoadModule(_moduleName);
        if( OpenALAPI::hModule == NULL )
        {
            LOG_ERROR("Failed to load Open AL API");
            return false;
        }
        
#define NANE_OAL_LOADFUNCTION( funcType, funcName )\
    OpenALAPI::funcName = reinterpret_cast<funcType>(DynamicModule::GetFunction(OpenALAPI::hModule, #funcName));\
	if( OpenALAPI::funcName == NULL)\
	{\
            LOG_ERROR("Failed to load '%1' function address\n") << #funcName;\
            return false;\
	}        
        NANE_OAL_LOADFUNCTION(LPALENABLE, alEnable);
        NANE_OAL_LOADFUNCTION(LPALDISABLE, alDisable);
        NANE_OAL_LOADFUNCTION(LPALISENABLED, alIsEnabled);
        NANE_OAL_LOADFUNCTION(LPALGETBOOLEAN, alGetBoolean);
        NANE_OAL_LOADFUNCTION(LPALGETINTEGER, alGetInteger);
        NANE_OAL_LOADFUNCTION(LPALGETFLOAT, alGetFloat);
        NANE_OAL_LOADFUNCTION(LPALGETDOUBLE, alGetDouble);
        NANE_OAL_LOADFUNCTION(LPALGETBOOLEANV, alGetBooleanv);
        NANE_OAL_LOADFUNCTION(LPALGETINTEGERV, alGetIntegerv);
        NANE_OAL_LOADFUNCTION(LPALGETFLOATV, alGetFloatv);
        NANE_OAL_LOADFUNCTION(LPALGETDOUBLEV, alGetDoublev);
        NANE_OAL_LOADFUNCTION(LPALGETSTRING, alGetString);
        NANE_OAL_LOADFUNCTION(LPALGETERROR, alGetError);
        NANE_OAL_LOADFUNCTION(LPALISEXTENSIONPRESENT, alIsExtensionPresent);
        NANE_OAL_LOADFUNCTION(LPALGETPROCADDRESS, alGetProcAddress);
        NANE_OAL_LOADFUNCTION(LPALGETENUMVALUE, alGetEnumValue);
        NANE_OAL_LOADFUNCTION(LPALLISTENERI, alListeneri);
        NANE_OAL_LOADFUNCTION(LPALLISTENERF, alListenerf);
        NANE_OAL_LOADFUNCTION(LPALLISTENER3F, alListener3f);
        NANE_OAL_LOADFUNCTION(LPALLISTENERFV, alListenerfv);
        NANE_OAL_LOADFUNCTION(LPALGETLISTENERI, alGetListeneri);
        NANE_OAL_LOADFUNCTION(LPALGETLISTENERF, alGetListenerf);
        NANE_OAL_LOADFUNCTION(LPALGETLISTENER3F, alGetListener3f);
        NANE_OAL_LOADFUNCTION(LPALGETLISTENERFV, alGetListenerfv);
        NANE_OAL_LOADFUNCTION(LPALGENSOURCES, alGenSources);
        NANE_OAL_LOADFUNCTION(LPALDELETESOURCES, alDeleteSources);
        NANE_OAL_LOADFUNCTION(LPALISSOURCE, alIsSource);
        NANE_OAL_LOADFUNCTION(LPALSOURCEI, alSourcei);
        NANE_OAL_LOADFUNCTION(LPALSOURCEF, alSourcef);
        NANE_OAL_LOADFUNCTION(LPALSOURCE3F, alSource3f);
        NANE_OAL_LOADFUNCTION(LPALSOURCEFV, alSourcefv);
        NANE_OAL_LOADFUNCTION(LPALGETSOURCEI, alGetSourcei);
        NANE_OAL_LOADFUNCTION(LPALGETSOURCEF, alGetSourcef);
        NANE_OAL_LOADFUNCTION(LPALGETSOURCEFV, alGetSourcefv);
        NANE_OAL_LOADFUNCTION(LPALSOURCEPLAYV, alSourcePlayv);
        NANE_OAL_LOADFUNCTION(LPALSOURCESTOPV, alSourceStopv);
        NANE_OAL_LOADFUNCTION(LPALSOURCEREWINDV, alSourceRewindv);
        NANE_OAL_LOADFUNCTION(LPALSOURCEPLAY, alSourcePlay);
        NANE_OAL_LOADFUNCTION(LPALSOURCEPAUSE, alSourcePause);
        NANE_OAL_LOADFUNCTION(LPALSOURCESTOP, alSourceStop);
        NANE_OAL_LOADFUNCTION(LPALSOURCEREWIND, alSourceRewind);
        NANE_OAL_LOADFUNCTION(LPALGENBUFFERS, alGenBuffers);
        NANE_OAL_LOADFUNCTION(LPALDELETEBUFFERS, alDeleteBuffers);
        NANE_OAL_LOADFUNCTION(LPALISBUFFER, alIsBuffer);
        NANE_OAL_LOADFUNCTION(LPALBUFFERDATA, alBufferData);
        NANE_OAL_LOADFUNCTION(LPALGETBUFFERI, alGetBufferi);
        NANE_OAL_LOADFUNCTION(LPALGETBUFFERF, alGetBufferf);
        NANE_OAL_LOADFUNCTION(LPALSOURCEQUEUEBUFFERS, alSourceQueueBuffers);
        NANE_OAL_LOADFUNCTION(LPALSOURCEUNQUEUEBUFFERS, alSourceUnqueueBuffers);
        NANE_OAL_LOADFUNCTION(LPALDISTANCEMODEL, alDistanceModel);
        NANE_OAL_LOADFUNCTION(LPALDOPPLERFACTOR, alDopplerFactor);
        NANE_OAL_LOADFUNCTION(LPALDOPPLERVELOCITY, alDopplerVelocity);
        NANE_OAL_LOADFUNCTION(LPALCGETSTRING, alcGetString);
        NANE_OAL_LOADFUNCTION(LPALCGETINTEGERV, alcGetIntegerv);
        NANE_OAL_LOADFUNCTION(LPALCOPENDEVICE, alcOpenDevice);
        NANE_OAL_LOADFUNCTION(LPALCCLOSEDEVICE, alcCloseDevice);
        NANE_OAL_LOADFUNCTION(LPALCCREATECONTEXT, alcCreateContext);
        NANE_OAL_LOADFUNCTION(LPALCMAKECONTEXTCURRENT, alcMakeContextCurrent);
        NANE_OAL_LOADFUNCTION(LPALCPROCESSCONTEXT, alcProcessContext);
        NANE_OAL_LOADFUNCTION(LPALCGETCURRENTCONTEXT, alcGetCurrentContext);
        NANE_OAL_LOADFUNCTION(LPALCGETCONTEXTSDEVICE, alcGetContextsDevice);
        NANE_OAL_LOADFUNCTION(LPALCSUSPENDCONTEXT, alcSuspendContext);
        NANE_OAL_LOADFUNCTION(LPALCDESTROYCONTEXT, alcDestroyContext);
        NANE_OAL_LOADFUNCTION(LPALCGETERROR, alcGetError);
        NANE_OAL_LOADFUNCTION(LPALCISEXTENSIONPRESENT, alcIsExtensionPresent);
        NANE_OAL_LOADFUNCTION(LPALCGETPROCADDRESS, alcGetProcAddress);
        NANE_OAL_LOADFUNCTION(LPALCGETENUMVALUE, alcGetEnumValue);
#undef NANE_OAL_LOADFUNCTION
        OpenALAPI::valid = true;
        return OpenALAPI::valid;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void ReleaseOpenALAPI()
    {
        OpenALAPI::valid = false;
        DynamicModule::UnloadModule(OpenALAPI::hModule);
        OpenALAPI::hModule = NULL;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}
